//
// Created by yyl on 2021/11/25.
//
#ifndef GLSL3LIB_BASESHADER_H
#define GLSL3LIB_BASESHADER_H

#include <GLES3/gl3.h>
#include <iostream>
#include <ImageDef.h>
#include "glmUtils.h"
#include "LogUtil.h"
#include "GLUtils.h"

#define MATH_PI 3.1415926535897932384626433832802

class BaseShader {
public:
    TransformMatrix transformMatrix;
    volatile bool isInit;
    GLuint programID;
    GLuint shaderVertexID;
    GLuint shaderFragmentID;
    GLuint VAO;
    GLuint VBO;
    GLuint EBO;
    GLuint textureID;
    NativeImage renderImage;
    bool isNeedUpdateTexture;
    bool imageEnable;
    int surfaceWidth;
    int surfaceHeight;

    char *shaderBufferVert;
    char *shaderBufferFrag;

    BaseShader() {
        isInit = false;
        programID = 0;
        shaderVertexID = 0;
        shaderFragmentID = 0;
        surfaceWidth = 0;
        surfaceHeight = 0;
        shaderBufferVert = nullptr;
        shaderBufferFrag = nullptr;
    }

    ~BaseShader() {
        LOGI("BaseShader 回收 ------");
        NativeImageUtil::FreeNativeImage(&renderImage);
        if (shaderBufferVert) {
            free(shaderBufferVert);
            shaderBufferVert = nullptr;
        }
        if (shaderBufferFrag) {
            free(shaderBufferFrag);
            shaderBufferFrag = nullptr;
        }
    }

    void LoadShaderScript(int shaderType, char *shaderFile, int strLen) {
        LOGI("LoadShaderScript shaderType=%d strLen=%d", shaderType, strLen);
        if (shaderType == 1) {
            shaderBufferVert = static_cast<char *>(malloc(strLen));
            memcpy(shaderBufferVert, shaderFile, strLen);
        } else if (shaderType == 2) {
            shaderBufferFrag = static_cast<char *>(malloc(strLen));
            memcpy(shaderBufferFrag, shaderFile, strLen);
        }

    };


    void createShaderFromFile() {
        if (shaderBufferVert != nullptr && shaderBufferFrag != nullptr) {
            LOGI("createShaderFromFile ok");
            programID = GLUtils::CreateProgram(shaderBufferVert, shaderBufferFrag, shaderVertexID,
                                               shaderFragmentID);
        }else{
            LOGI("createShaderFromFile 没有shader文件");
        }
    }

    //纹理环绕 参数
    // GL_REPEAT	对纹理的默认行为。重复纹理图像。
    // GL_MIRRORED_REPEAT	和GL_REPEAT一样，但每次重复图片是镜像放置的。
    // GL_CLAMP_TO_EDGE	纹理坐标会被约束在0到1之间，超出的部分会重复纹理坐标的边缘，产生一种边缘被拉伸的效果。
    // GL_CLAMP_TO_BORDER	超出的坐标为用户指定的边缘颜色。 需要配置颜色glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
    //纹理过滤 参数
    //GL_NEAREST邻近过滤 (取最近的1个像素->更清晰看到像素色块,图像会有像素方块)
    //GL_LINEAR 线性过滤 (取最近的4个像素混合生成->更平滑的图案,图像会羽化模糊)
    void createTexture() {
        glGenTextures(1, &textureID);
        glBindTexture(GL_TEXTURE_2D, textureID);
        //为纹理设置环绕方式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        //为纹理设置过滤方式      多级渐远纹理只能用于缩小
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glBindTexture(GL_TEXTURE_2D, GL_NONE);
    }


    void LoadImage(NativeImage *imageSrc) {
        LOGI("BaseShader::LoadImage 复制到渲染对像 imageSrc = %p", imageSrc->ppPlane[0]);
        renderImage.width = imageSrc->width;
        renderImage.height = imageSrc->height;
        renderImage.format = imageSrc->format;
        NativeImageUtil::CopyNativeImage(imageSrc, &renderImage);
        isNeedUpdateTexture=true;
    };

    void bindTextureImage() {
        if (isNeedUpdateTexture&&renderImage.ppPlane[0] != nullptr) {
            LOGI("bindTextureImage 纹理");
            glBindTexture(GL_TEXTURE_2D, textureID);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, renderImage.width, renderImage.height, 0,
                         GL_RGBA,
                         GL_UNSIGNED_BYTE, renderImage.ppPlane[0]);
            //自动生成所有需要的多级渐远纹理   多级渐远纹理是缩小时图像采样的方式
            //生成了纹理和相应的多级渐远纹理后，需要释放图像的内存
            glGenerateMipmap(GL_TEXTURE_2D);
            NativeImageUtil::FreeNativeImage(&renderImage);
            glBindTexture(GL_TEXTURE_2D, GL_NONE);
        }else{
            LOGI("bindTextureImage 没有纹理");
        }

    }

    void createShaderFromString(char *vertexString, char *fragmentString) {
        LOGI("createShaderFromString ");
        programID = GLUtils::CreateProgram(vertexString, fragmentString, shaderVertexID,
                                           shaderFragmentID);
    }

    void Destroy() {
        if (programID) {
            glDeleteProgram(programID);
            programID = GL_NONE;
        }
        if (VAO) {
            glDeleteVertexArrays(1, &VAO);
            VAO = GL_NONE;
        }
        if (VBO) {
            glDeleteBuffers(1, &VBO);
            VBO = GL_NONE;
        }
        if (EBO) {
            glDeleteBuffers(1, &EBO);
            EBO = GL_NONE;
        }
        if (textureID) {
            glDeleteTextures(1, &textureID);
            textureID = GL_NONE;
        }
    }


    virtual void Init() = 0;

    virtual void Draw(int screenW, int screenH) = 0;


private:


};


#endif //GLSL3LIB_BASESHADER_H
